package execl

import (
	"fmt"
	"github.com/tealeg/xlsx"
	"reflect"
	"time"
)

// SaveStructToExcel 将结构体数组保存为 Excel 文件，标题由结构体字段名生成。
func SaveStructToExcel(path string, data interface{}) error {
	// 创建新的工作簿
	wb := xlsx.NewFile()
	sheet, err := wb.AddSheet("Data")
	if err != nil {
		return err
	}

	// 检查数据是否为空
	sliceValue := reflect.ValueOf(data)
	if sliceValue.Len() == 0 {
		return wb.Save(path)
	}

	// 获取结构体的字段名作为标题
	elemType := getElementType(sliceValue.Type())
	fields, err := getFields(elemType)
	if err != nil {
		return err
	}
	addHeader(sheet, fields)

	// 遍历每个元素，添加数据行
	for i := 0; i < sliceValue.Len(); i++ {
		item := sliceValue.Index(i).Interface()
		rowValues, err := getRowValues(item, fields)
		if err != nil {
			return err
		}
		addRow(sheet, rowValues)
	}

	// 保存 Excel 文件
	return wb.Save(path)
}

// getElementType 获取结构体指针或切片的元素类型。
func getElementType(t reflect.Type) reflect.Type {
	if t.Kind() == reflect.Ptr || t.Kind() == reflect.Slice {
		return t.Elem()
	}
	return t
}

// getFields 从结构体中提取字段名，支持xlsx标签指定列名。
func getFields(t reflect.Type) ([]string, error) {
	fields := make([]string, 0)
	for i := 0; i < t.NumField(); i++ {
		field := t.Field(i)
		if tag := field.Tag; tag != "" {
			// 检查xlsx标签
			_, ok := tag.Lookup("xlsx")
			if ok {
				name, err := getTagValue(tag, "name")
				if err == nil && name != "" {
					fields = append(fields, name)
					continue
				}
			}
		}
		// 如果没有xlsx标签，使用字段名
		fields = append(fields, field.Name)
	}
	return fields, nil
}

// getTagValue 从反射标签中获取指定键的值。
func getTagValue(tag reflect.StructTag, key string) (string, error) {
	value, ok := tag.Lookup(key)
	if !ok || value == "" {
		return "", fmt.Errorf("tag %s not found in field", key)
	}
	return value, nil
}

// addHeader 添加标题行到工作表。
func addHeader(sheet *xlsx.Sheet, headers []string) {
	row := make([]string, len(headers))
	copy(row, headers)
	addRow(sheet, row)
}

// getRowValues 将结构体项转换为 Excel 行数据。
func getRowValues(item interface{}, fields []string) ([]string, error) {
	values := make([]string, len(fields))
	for i, fieldName := range fields {
		value, err := getValue(item, fieldName)
		if err != nil {
			return nil, err
		}
		//转换
		values[i] = valueToString(value)
	}
	return values, nil
}

// getValue 根据字段名获取结构体项的值。
func getValue(item interface{}, fieldName string) (reflect.Value, error) {
	v := reflect.ValueOf(item)
	if v.Kind() == reflect.Ptr && !v.IsNil() {
		v = v.Elem()
	}
	if v.Kind() != reflect.Struct {
		return reflect.Value{}, fmt.Errorf("item is not a struct")
	}

	field, b := v.Type().FieldByName(fieldName)
	if b == false {
		return reflect.Value{}, fmt.Errorf("field %s not found", fieldName)
	}

	value := v.FieldByIndex(field.Index)
	// 处理指针字段，如果需要的话
	if field.Type.Kind() == reflect.Ptr && !value.IsNil() {
		// 如果是嵌套结构体指针，递归获取值？
		// 这部分逻辑可能需要更复杂的处理。
		return value, nil
	}
	return value, nil
}

// addRow 添加数据行到工作表。
func addRow[T any](sheet *xlsx.Sheet, row []T) {
	r := sheet.AddRow()
	r.WriteSlice(&row, len(row))
}

func writeRows(row *xlsx.Row, value reflect.Value) {
	for j := 0; j < value.NumField(); j++ {
		//if j==0{
		//	continue
		//}
		cell := row.AddCell()
		if value.Field(j).Type().String() == "time.Time" {
			cell.Value = fmt.Sprintf("%v", value.Field(j).Interface().(time.Time))
			continue
		}
		if value.Field(j).Type().String() == "float64" {
			cell.Value = fmt.Sprintf("%.2f", value.Field(j).Interface().(float64))
			continue
		}
		if value.Field(j).Type().String() == "int" {
			cell.Value = fmt.Sprintf("%v", value.Field(j).Interface().(int))
			continue
		}
		if value.Field(j).Type().String() == "float32" {
			cell.Value = fmt.Sprintf("%.2f", value.Field(j).Interface().(float32))
			continue
		}
		cell.Value = value.Field(j).Convert(reflect.TypeOf("")).String()

	}
}

// 将反射的value转换成相应的字符串
func valueToString(value reflect.Value) string {
	if value.Type().String() == "time.Time" {
		return fmt.Sprintf("%v", value.Interface().(time.Time))

	}
	if value.Type().String() == "float64" {
		return fmt.Sprintf("%.2f", value.Interface().(float64))

	}
	if value.Type().String() == "int" {
		return fmt.Sprintf("%v", value.Interface().(int))

	}
	if value.Type().String() == "float32" {
		return fmt.Sprintf("%.2f", value.Interface().(float32))

	}
	return value.Convert(reflect.TypeOf("")).String()
}
